Loading...
 

MorphIT Asset Routing

MorphIT Asset Routing

4.4.0

The asset routing of MorphIT was fundamentally revised in version 4.4.0. The path configuration has been designed in such a way that the previous configuration can continue to be used without modification. However, if you want to use one of the new features, it must be rewritten to the new notation.

Features

public/private assets

The new path configuration allows the asset paths to be divided into public and private assets. The former MorphIT version only knows public assets. These are the files that the MorphIT server delivers without authentication to any HTTP client that knows the correct URL. The URL contains the path to the delivered file in plain text, which also means that you can try to guess valid paths even if ClassiX did not deliver the path. (Example: If there is /public/icons/bild_2.bmp, then there is probably also /public/icons/bild_1.bmp)

Private assets are protected in two ways. Firstly, the HTTP client needs a cookie to access these assets, which is assigned as soon as a web socket connection with the ClassiX instance has been established, i.e. after login. Second, the paths are encrypted with a key, which only the issuing ClassiX instance and the MorphIT server know. So it is impossible to guess another valid private path from an encrypted private path (e.g. /private/assets/zgEAAADxJKgk3G86gwAtR5tKX9nKWP3RpEeqG74adFGt3gtE8A) even if the plaintext is known, or to derive information from the path structure.

All asset paths follow the structure: /category/Id/ Path
Examples:

URL Category Id Path
/private/assets/zgEAAADxJKgk3G86gwAtR5tK... private assets zgEAAADxJKgk3G86gwAtR5tK...
/public/icons/image_2.bmp public icons image_2.bmp
/public/icons/myapp/custom/image.png public icons myapp/custom/image.png

Key exchange and encryption

The MorphIT server generates a random 128 bit key for each ClassiX instance, which is communicated to the ClassiX instance during registration. As soon as a MorphIT Client is connected to this ClassiX instance, it receives a cookie (random SHA-256 hash), to which (at the server) is stored, to which ClassiX instances the Client is connected, so that it can use this cookie to decrypt only the paths, which were generated by its own ClassiX instances. If such an instance now wants to issue a path to a private asset, the path is encrypted according to the following scheme

base64url(<xor-checksum:uint8>,<cx-id:uint32-le>,aes-128-cbc((<path>,';',<path.length%256:uint8>),<key>))

Before encryption, the path is supplemented by the character ; and the byte length of the path modulo 256. These two values are checked in the server during decryption to ensure that the path was decrypted with the correct key. This check is necessary because the part that determines the key to be used is itself not encrypted and could be manipulated by an attacker.

The path thus added is now encrypted with the exchanged key and AES in Cipher Block Chaining (CBC) mode. A NULL block (1 block = 8 bytes) is used as initialisation vector (IV) for CBC for each path. AES was chosen because, unlike simpler encryption schemes (such as XOR), a known plaintext attack cannot be used to obtain information about the key. The CBC mode is used to prevent encrypted blocks from being exchanged between two paths in order to recreate a valid encrypted path (which would be possible with ECB). As soon as a plaintext block in CBC mode differs from another path, all subsequent blocks also differ.

It is generally not recommended in guidelines to use an IV that is known and the same for all messages to be encrypted. Implementing this anyway has the following consequence: If the first n blocks of two paths are identical, then the first n encrypted blocks are also identical. An attacker can thus recognise whether two paths have the same prefix. We consider this problem to be uncritical when encrypting paths, since identical prefixes in paths are not security-relevant information.

To prevent this would mean to use a randomly generated IV for each private path, which would have to be coded into this path and would thus extend the path by 11 clear text bytes (8 bytes -> 11 bytes in Base64). Since the IV is in the path and thus under the control of the attacker, the attacker could, in the case of paths that consist of only one block and whose plaintext he knows, generate any other paths (with block length 1) in the path by manipulating the IV, which are also accepted as valid. Thus, in the case of very short paths and knowledge of a path, the attacker could navigate freely, which was originally intended to prevent encryption. Because of the additional effort and the fact that the security is actually lower, the constant IV is chosen, which cannot be manipulated by the attacker.

The encrypted path is then preceded by the ID of the ClassiX instance and a 1-byte XOR checksum of the entire message. The ClassiX ID is encoded into the path, because each ClassiX instance uses a different key and so the server can assign the path to the key. Furthermore the server can check if the transmitted cookie is connected to this ClassiX instance. This information has to be stored in the path and not in the cookie, because the application may be logged in in several browser tabs and all browser tabs share their cookies. A clear assignment of cookie → ClassiX is therefore not possible. The XOR checksum is to ensure that the Base64 encoding of the path is correct and that invalid paths are detected before decoding if possible.

This binary, encrypted path is finally encoded with base64url , leaving out the padding bytes.

The server returns the same error message for both valid paths whose file was not found and invalid paths, so a Padding Oracle attack cannot be used to break AES encryption without knowing the key. The server only reports error causes if the dev.asset_errors and dev.server_errors options intended for development are set in the server configuration.

Cookies?
The cookie, which is required to access the private assets, is only set after login. Cookies are the only mechanism supported by the browser to authenticate an HTTP client across multiple requests.
If no private assets are configured in the MorphIT server, no cookie is set after login, as before.

Reference private assets from private assets

The paths of private assets are issued to the browser completely encrypted. This means that the browser cannot perform relative navigation, which is also intended for private assets (e.g.: /private/assets/zgEAAADxJKgk3G86gwAtR5tKX9nKWP3RpEeqG74adFGt3gtE8A/../style.css is not a valid path). Since generating a correct private path requires both the ClassiX ID and the key of this instance, static assets (e.g. an .html file) cannot contain valid private paths. Nevertheless, to allow, for example, a private .html asset to reference a private .css asset, predefined file types (.html, .css & .js) are interpreted by the server before being sent to the client. In the process, EJS-like tags within the files are evaluated.

Example of an .html file that references image files.

Privateassetejs

These special tags are only interpreted within private assets. Since the link to a private asset can only have been issued by a ClassiX instance and the link contains the ID of the ClassiX instance, the server knows for sure at this point that the client requesting the private asset is authenticated and which key must be used to generate private assets for the same ClassiX instance. Since neither applies to public assets, these tags are not evaluated within public assets.

Within the brackets <%= and =%> the following functions can be used, which replace the whole bracket with the corresponding path.
The arguments are passed in single brackets and can optionally be enclosed in " " or ' '.

Function Description
public The transferred path should have the form Id/path and is only extended by public/. If Id is not defined in the public area of the asset configuration , the tag is replaced to INVALID_PUBLIC_OR_PRIVATE_PATH!!! instead.
private The transferred path should have the form Id/path and is extended by private/ and the path is encrypted. If Id is not defined in the private-area of the asset configuration, the tag is replaced to INVALID_PUBLIC_OR_PRIVATE_PATH!!! instead.
relative The path passed should be a file system path relative to the current file. The referenced target file should be located in a directory defined in the asset configuration as either public or private. Depending on the category of the asset path, the path is generated as a public path or private path. If the specified path is not part of the asset configuration, the tag is replaced by INVALID_ABSOLUTE_OR_RELATIVE_PATH!!!
absolute The passed path should be an absolute file system path on the machine where the current file is located. The referenced target file should be located in a directory defined in the asset configuration as either public or private. Depending on the category of the asset path, the path is generated as either a public path or a private path. If the specified path is not part of the asset configuration, the tag is replaced by INVALID_ABSOLUTE_OR_RELATIVE_PATH!!!

Tags that use unknown or syntactically incorrect functions are not interpreted by the server and are issued unchanged.

Note: Because private assets interpreted by the server (.js, .css, .html) represent dynamic content, the E-Tag and Content-Length headers are not set by the server. As a result, the browser cannot cache the resources and does not display a progress bar when downloading larger files.

local/remote assets

The new path configuration now allows the MorphIT server to deliver assets from another machine as long as there is a MorphIT Launcher process running that is connected to the server. For each asset path it is possible to optionally specify on which machine the assets are located. If a file is requested from a remote path, the server forwards the request to the launcher, which pushes the file to the server, which in turn forwards it to the client.

RemoteAssets

Note: The file request is made through the web socket already established, but the file itself is transferred through a new HTTP-PUT request. This request is started on the port port or(externalPort) from the loaded config.js It is important that these values match in the server configuration and the launch configuration so that the file can be transferred correctly.

static/dynamic path configuration

The static path configuration is made in config.js and is described in the "Configuration" section. The static path configuration defines the same path configuration for all ClassiX instances and is the right choice for most MorphIT applications.

The dynamic path configuration has been developed for more complex scenarios and is activated by setting cloud.enabled=true in config.js. This switches the server into cloud mode and evaluates additional query parameters for asset requests, which are defined in cloud.routing_parameters (default: "client", "product"). The idea of the cloud mode is that the MorphIT server is preceded by a reverse proxy that rewrites paths according to a predefined scheme, thus making it possible to run several different MorphIT applications with one MorphIT server. This configuration is used in the classix.cloud. The path classix.cloud/ remains classix.cloud/, but classix.cloud/direct/classix/qm/ becomes classix.cloud/?client=classix&product=qm This mapping also affects all assets. The dynamic path configuration is now used to tell the server where the assets are located for paths such as classix.cloud/public/icons/icon1.png?client=classix&product=qm. Depending on the query parameters, the server delivers different assets for the same paths.

The dynamic path configuration is set via the server command set_path_map. This is best done by the web service instance once it has been selected as the web service instance. The pathmap to be set has the following structure:

{ "public": {...} "private": {...}, "classix": { "public":{...} "private":{...} "qm": { "public": {...}, "private": {...} }, "gestin": { "public": {...}, "private": {...} } }, "customer3": {...}, ... }

The number of levels is defined by the number of cloud.routing_parameters and the levels are defined in the same order. The first level is client in the default configuration and the second is product. On each level the paths for the categories public and private can be defined as in the static path configuration. The lower levels "inherit" the path definitions from the levels above, so that the public/private definitions on the top level apply to all client/product combinations.

If an asset is requested for a client/product combination not specified in the path configuration, the path map is generated from the last known node (if client is defined, then from it, otherwise from the public/private of the top level).

Important: Even with activated cloud mode, the static path configuration is used for queries without query parameters. This cannot be overwritten by set_path_map and is not considered for queries with query parameters. The dynamic and static configuration are not merged.

Note: as of MorphIT version 4.11.3, the option cloud.apply_static_path_map can be set, whereby the paths defined in the static path configuration always apply. set_path_map can in this case only additively extend the paths for certain query parameters.

If the dynamic path configuration is used, a ClassiX instance to be started must also be informed about the path configuration to be used by it, so that absolute paths can be correctly converted into relative URLs. This information has to be given to the server at request_binding in the asset_route parameter. The corresponding path configuration is communicated to the started ClassiX instance by the server after registration.

ClassiX instances, which are automatically pre-started by the Launcher, always receive the static path configuration from the server.

Configuration

The static asset configuration is done in config.js. The new key paths exists for this purpose. The default value auto defines a path configuration that is equivalent to the previous one and refers to the variables iconPath, bmpPath, widgetPath and assetsPath.

The generated configuration looks as follows:

'paths': { 'public':{ 'icons': {'path':'${iconPath}', 'alias':'/assets/images/icons'}, 'bmp': {'path':'${bmpPath}', 'alias':'/assets/images/bmp'}, 'widgets': {'path':'${widgetPath}', 'alias':'/widgets'}, 'assets': {'path':'${assetsPath}', 'alias':['/assets/images/Projects/Data/Assets','/Projects/Data/Assets'], 'cache':false}, }, 'private': false // could be {...} to define and enable private paths }
Important: Each path configuration must define the path /public/widgets/, otherwise WebWidgets cannot be used.

Structure

paths

Field Type Description
public Object category object, which defines the public paths.
private Object | false category object, which defines the private paths or false if private paths should be disabled.

category

Field Type Description
id Object

A pathdefinition object that defines for the specified id where and how the server should deliver the files.
Except for widgets the id has no special semantics and can be freely chosen. It is also allowed to use the same id in different categories.

/public/widgets/ must contain the sources of the WebWidgets to be delivered. (Previously: the widgetPath config variable)

pathdefinition

Field Type Description
path string | Array The absolute path to the file system directory from which the files are to be delivered.
If several paths are specified here in an array, then all specified directories are searched for the file.
host string The machine on which the files are stored (see: remote assets). The machine can be specified by host name or IP address. If the machine is not the local machine of the server, a launcher process must be running there to deliver the files. This specification is optional and the default is localhost.
alias string | Array This can be used to define one or more alias paths via which the asset path can also be accessed. This field is only useful for public assets and is mainly intended for migration. If, for example, /alias is defined as an alias, then the assets are also accessible under /alias in addition to /public/id. The specification is optional and without specification no alias paths are defined.
cache bool Can be set to false to prevent the browser from caching the assets. In this case, the server sets HTTP headers that instruct the browser not to cache the files. The specification is optional and the default is true.